package com.quectel.cms.controller.datav;

import cn.hutool.core.date.DateUtil;
import com.quectel.cms.controller.BaseController;
import com.quectel.cms.util.SessionHolder;
import com.quectel.cms.vo.datav.FireSafetyAlarmEventVo;
import com.quectel.cms.vo.datav.FireSafetyAlarmTrendVo;
import com.quectel.cms.vo.datav.FireSafetyCountDeviceVo;
import com.quectel.constant.core.ResourceGroupCodeConstants;
import com.quectel.constant.global.SystemConstants;
import com.quectel.core.module.alarm.dto.AlarmRuleDto;
import com.quectel.core.module.alarm.dto.AlarmRuleTenantConditionDto;
import com.quectel.core.module.alarm.service.AlarmRuleService;
import com.quectel.core.module.alarm.service.AlarmRuleTenantConditionService;
import com.quectel.core.module.device.dto.DeviceCategoryDto;
import com.quectel.core.module.device.dto.DeviceDto;
import com.quectel.core.module.device.dto.DeviceTypeDto;
import com.quectel.core.module.device.dto.datav.DeviceTypeCountDto;
import com.quectel.core.module.device.dto.mongo.MongoDeviceAlarmLogDto;
import com.quectel.core.module.device.service.DeviceCategoryService;
import com.quectel.core.module.device.service.DeviceService;
import com.quectel.core.module.device.service.mongo.MongoDeviceService;
import com.quectel.core.module.resource.dto.ResourceGroupDto;
import com.quectel.core.module.resource.service.ResourceGroupService;
import com.quectel.util.common.*;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author rananxin
 * @date 2021/12/6 1:46 下午
 */
@RestController
@RequestMapping("datav/fireSafety")
@Tag(name = "DataVFireSafetyController", description = "大屏消防安全相关接口")
public class DataVFireSafetyController extends BaseController {

    @DubboReference
    private DeviceService deviceService;
    @DubboReference
    private DeviceCategoryService deviceCategoryService;
    @DubboReference
    private AlarmRuleService alarmRuleService;
    @DubboReference
    private AlarmRuleTenantConditionService alarmRuleTenantConditionService;
    @DubboReference
    private MongoDeviceService mongoDeviceService;
    @DubboReference
    private ResourceGroupService resourceGroupService;

    @GetMapping("countDevice")
    @Operation(summary = "消防设备统计")
    public Response<FireSafetyCountDeviceVo> countDevice(
            @Parameter(description = "小区id 非必填") @RequestParam(required = false) Long villageId
    ) {
        FireSafetyCountDeviceVo result = new FireSafetyCountDeviceVo();

        Map<String, Object> params = Request.parseParams(httpServletRequest);

        super.fillAuthorityData(params);

        super.fillResourceGroupCode(params, ResourceGroupCodeConstants.FIRE_SAFETY_GROUP);

        List<DeviceTypeCountDto> deviceTypeCountDtos = deviceService.countDeviceType(params);
        Map<String, List<DeviceTypeCountDto>> categoryCodeMap = deviceTypeCountDtos.stream()
                .filter(o -> StringUtils.isNotBlank(o.getDeviceCategoryCode()))
                .collect(Collectors.groupingBy(DeviceTypeCountDto::getDeviceCategoryCode));


        int deviceCount = 0, alarmCount = 0, normalCount = 0;
        for (String key : categoryCodeMap.keySet()) {
            FireSafetyCountDeviceVo.DeviceCountItem item = new FireSafetyCountDeviceVo.DeviceCountItem();

            // 计数器数组 索引位置分别对应total onlineCount offlineCount unAlarmCount alarmCount normalCount
            int[] countArr = {0, 0, 0, 0, 0, 0};
            List<DeviceTypeCountDto> typeDeviceList = categoryCodeMap.get(key);
            for (DeviceTypeCountDto deviceTypeCountDto : typeDeviceList) {
                countArr[0] += deviceTypeCountDto.getTotal();
                countArr[1] += deviceTypeCountDto.getOnlineCount();
                countArr[2] += deviceTypeCountDto.getOfflineCount();
                countArr[3] += deviceTypeCountDto.getUnAlarmCount();
                countArr[4] += deviceTypeCountDto.getAlarmCount();
                countArr[5] += deviceTypeCountDto.getNormalCount();
            }

            item.setCode(typeDeviceList.get(0).getDeviceCategoryCode());
            item.setName(typeDeviceList.get(0).getDeviceCategoryName());
            item.setTotal(countArr[0]);
            item.setOnlineCount(countArr[1]);
            item.setOfflineCount(countArr[2]);
            item.setUnAlarmCount(countArr[3]);
            item.setAlarmCount(countArr[4]);
            item.setNormalCount(countArr[5]);
            result.getDeviceCountItems().add(item);

            deviceCount += countArr[0];
            alarmCount += countArr[4];
            normalCount += countArr[5];
        }

        result.setDeviceCount(deviceCount);
        result.setNormalCount(normalCount);
        result.setAlarmCount(alarmCount);
        return Response.<FireSafetyCountDeviceVo>ok().wrap(result);
    }

    @GetMapping("alarmTrend")
    @Operation(summary = "设备告警统计")
    public Response<FireSafetyAlarmTrendVo> alarmTrend(
            @Parameter(description = "小区id 必填") @RequestParam Long villageId,
            @Parameter(description = "统计范围 1近一个月 2近半年 3近一年 必填") @RequestParam Byte range
    ) {
        long currentTimeMillis = System.currentTimeMillis();

        FireSafetyAlarmTrendVo result = new FireSafetyAlarmTrendVo();

        String startDate = null;
        String endDate = null;
        // 根据range计算x轴数据
        String[] xData = null;
        DateUtils.FormatType formatType = null;
        switch (range) {
            case 1:
                formatType = DateUtils.FormatType.SIMPLE;
                xData = DateUtils.timeSlotOfDay(DateUtil.beginOfDay(DateUtil.lastMonth())
                        , new Date(currentTimeMillis), formatType, 1);
                startDate = xData[0] + " 00:00:00";
                endDate = xData[xData.length - 1] + " 23:59:59";
                break;
            case 2:
                formatType = DateUtils.FormatType.MONTH;
                xData = DateUtils.timeSlotOfMonth(DateUtil.beginOfMonth(DateUtil.offsetMonth(new Date(currentTimeMillis), -6))
                        , new Date(currentTimeMillis), formatType, 1);
                startDate = xData[0] + "-01 00:00:00";
                try {
                    endDate = DateUtils.format(DateUtil.endOfMonth(new SimpleDateFormat(formatType.getType()).parse(xData[xData.length - 1])), DateUtils.FormatType.COMMON);
                } catch (ParseException e) {
                    throw new RuntimeException(e);
                }

                break;
            case 3:
                formatType = DateUtils.FormatType.MONTH;
                xData = DateUtils.timeSlotOfMonth(DateUtil.beginOfMonth(DateUtil.offsetMonth(new Date(currentTimeMillis), -12))
                        , new Date(currentTimeMillis), formatType, 1);

                startDate = xData[0] + "-01 00:00:00";
                try {
                    endDate = DateUtils.format(DateUtil.endOfMonth(new SimpleDateFormat(formatType.getType()).parse(xData[xData.length - 1])), DateUtils.FormatType.COMMON);
                } catch (ParseException e) {
                    throw new RuntimeException(e);
                }

                break;
            default:
                xData = new String[]{};
                break;
        }
        result.setTransverseAxis(xData);

        Map<String, Object> params = Request.parseParams(httpServletRequest);
        super.fillAuthorityData(params);
        super.fillResourceGroupCode(params, ResourceGroupCodeConstants.FIRE_SAFETY_GROUP);

        List<FireSafetyAlarmTrendVo.AlarmTreadItem> alarmTreadItems = new ArrayList<>();
        result.setAlarmTreadItems(alarmTreadItems);

        List<FireSafetyAlarmTrendVo.AlarmRuleItem> alarmRuleItems = new ArrayList<>();
        result.setAlarmRuleItems(alarmRuleItems);

        //消防种类
        ResourceGroupDto resourceGroupDto = resourceGroupService.selectByCacheCode(ResourceGroupCodeConstants.FIRE_SAFETY_GROUP);


        if (range.intValue() == 1) {

            for (int i = 0; i < xData.length; i++) {
                params.put(SystemConstants.TIME_SLOT_KEY_PAIR.getLeft(), xData[i] + " 00:00:00");
                params.put(SystemConstants.TIME_SLOT_KEY_PAIR.getRight(), xData[i] + " 23:59:59");
                //按照$deviceDto.deviceCategoryId
                Map<String, Integer> groupCount = mongoDeviceService.groupDeviceAlarmLogCount(params,
                        "$deviceEntity.deviceCategoryId");

                for (DeviceCategoryDto deviceCategory : resourceGroupDto.getDeviceCategorys()) {
                    if (i <= 0) {
                        //第一次需要初始化
                        FireSafetyAlarmTrendVo.AlarmTreadItem item = new FireSafetyAlarmTrendVo.AlarmTreadItem();
                        item.setCode(deviceCategory.getCode());
                        item.setName(deviceCategory.getName());
                        Integer[] yValues = new Integer[xData.length];
                        yValues[i] = groupCount.get(String.valueOf(deviceCategory.getId())) == null ? 0 : groupCount.get(String.valueOf(deviceCategory.getId()));
                        item.setLongitudinalAxis(yValues);
                        alarmTreadItems.add(item);
                    } else {
                        FireSafetyAlarmTrendVo.AlarmTreadItem item = alarmTreadItems.stream().filter(v -> Objects.equals(deviceCategory.getCode(), v.getCode())).collect(Collectors.toList()).get(0);
                        item.getLongitudinalAxis()[i] = groupCount.get(String.valueOf(deviceCategory.getId())) == null ? 0 : groupCount.get(String.valueOf(deviceCategory.getId()));
                    }
                }
            }
        } else {
            for (int i = 0; i < xData.length; i++) {
                params.put(SystemConstants.TIME_SLOT_KEY_PAIR.getLeft(), xData[i] + "-01 00:00:00");
                try {
                    params.put(SystemConstants.TIME_SLOT_KEY_PAIR.getRight(), DateUtils.format(DateUtil.endOfMonth(new SimpleDateFormat(formatType.getType()).parse(xData[i])), DateUtils.FormatType.COMMON));
                } catch (ParseException e) {
                    throw new RuntimeException(e);
                }
                //按照$deviceDto.deviceCategoryId
                Map<String, Integer> groupCount = mongoDeviceService.groupDeviceAlarmLogCount(params,
                        "$deviceEntity.deviceCategoryId");

                for (DeviceCategoryDto deviceCategory : resourceGroupDto.getDeviceCategorys()) {
                    if (i <= 0) {
                        //第一次需要初始化
                        FireSafetyAlarmTrendVo.AlarmTreadItem item = new FireSafetyAlarmTrendVo.AlarmTreadItem();
                        item.setCode(deviceCategory.getCode());
                        item.setName(deviceCategory.getName());
                        Integer[] yValues = new Integer[xData.length];
                        yValues[i] = groupCount.get(String.valueOf(deviceCategory.getId())) == null ? 0 : groupCount.get(String.valueOf(deviceCategory.getId()));
                        item.setLongitudinalAxis(yValues);
                        alarmTreadItems.add(item);
                    } else {
                        FireSafetyAlarmTrendVo.AlarmTreadItem item = alarmTreadItems.stream().filter(v -> Objects.equals(deviceCategory.getCode(), v.getCode())).collect(Collectors.toList()).get(0);
                        item.getLongitudinalAxis()[i] = groupCount.get(String.valueOf(deviceCategory.getId())) == null ? 0 : groupCount.get(String.valueOf(deviceCategory.getId()));
                    }
                }
            }
        }

        //按照告警种类去查询
        //按照$deviceCurrentAlarmDto.alarmRule.code
        Map<String, Object> queryAlarmCodeCountParams = Request.parseParams(httpServletRequest);
        super.fillAuthorityData(queryAlarmCodeCountParams);
        super.fillResourceGroupCode(queryAlarmCodeCountParams, ResourceGroupCodeConstants.FIRE_SAFETY_GROUP);
        params.put(SystemConstants.TIME_SLOT_KEY_PAIR.getLeft(), startDate);
        params.put(SystemConstants.TIME_SLOT_KEY_PAIR.getRight(), endDate);

        Map<String, Integer> group = mongoDeviceService.groupDeviceAlarmLogCount(params,
                "$deviceCurrentAlarmEntity.ruleId");

        for (Map.Entry<String, Integer> entry : group.entrySet()) {
            if (entry.getKey() == null) {
                continue;
            }
            AlarmRuleDto alarmRuleDto = alarmRuleService.selectById(Long.valueOf(entry.getKey()));
            if (alarmRuleDto == null) {
                continue;
            }
            FireSafetyAlarmTrendVo.AlarmRuleItem item = new FireSafetyAlarmTrendVo.AlarmRuleItem();
            item.setCode(entry.getKey());

            AlarmRuleTenantConditionDto alarmRuleTenantConditionDto = alarmRuleTenantConditionService.selectByTenantIdAndRuleId(SessionHolder.getSysUser().getTenantId(), alarmRuleDto.getId());
            if (alarmRuleTenantConditionDto != null) {
                AlarmRuleDto alarm = JacksonUtils.parse(alarmRuleTenantConditionDto.getAlarmRuleConditionJson(), AlarmRuleDto.class);
                if (alarm != null && Objects.equals(SystemConstants.YES.intValue(), alarm.getStatus())) {
                    item.setName(alarm.getName());
                }

            }
            if (item.getName() == null) {
                //如果租户没有配置
                item.setName(alarmRuleDto.getName());
            }
            item.setCount(entry.getValue());
            alarmRuleItems.add(item);
        }

        return Response.<FireSafetyAlarmTrendVo>ok().wrap(result);
    }

    @GetMapping("alarmEvent")
    @Operation(summary = "告警事件信息流")
    public Response<FireSafetyAlarmEventVo> alarmEvent(
            @Parameter(description = "小区id 必填") @RequestParam Long villageId,
            @Parameter(description = "从1开始的页码数") @RequestParam Integer pageNo,
            @Parameter(description = "每页多少条") @RequestParam Integer pageSize
    ) {
        FireSafetyAlarmEventVo result = new FireSafetyAlarmEventVo();

        long currentTimeMillis = System.currentTimeMillis();

        Request request = Request.configParams(Request.parseParams(httpServletRequest));

        super.fillAuthorityData(request.getParams());
        super.fillResourceGroupCode(request.getParams(), ResourceGroupCodeConstants.FIRE_SAFETY_GROUP);

        int total = mongoDeviceService.queryDeviceAlarmLogTotal(request.getParams());
        List<MongoDeviceAlarmLogDto> mongoDeviceAlarmLogDtos = new ArrayList<>();
        if (total > 0) {
            mongoDeviceAlarmLogDtos = mongoDeviceService.queryDeviceAlarmLog(request.getParams());
        }

        List<FireSafetyAlarmEventVo.AlarmEventItem> voList = mongoDeviceAlarmLogDtos.stream().map(o -> {
            FireSafetyAlarmEventVo.AlarmEventItem alarmEventItem = new FireSafetyAlarmEventVo.AlarmEventItem();
            DeviceDto deviceDto = o.getDeviceDto();
            if (deviceDto != null) {
                alarmEventItem.setDeviceId(deviceDto.getId());
                alarmEventItem.setDeviceUniqueId(deviceDto.getUniqueId());
                alarmEventItem.setDeviceName(deviceDto.getName());
                DeviceTypeDto deviceType = deviceDto.getDeviceType();
                if (deviceType != null) {
                    alarmEventItem.setDeviceTypeCode(deviceType.getCode());
                    alarmEventItem.setDeviceTypeName(deviceType.getName());
                }
                DeviceCategoryDto deviceCategory = deviceDto.getDeviceCategory();
                if (deviceCategory != null) {
                    alarmEventItem.setDeviceCategoryCode(deviceCategory.getCode());
                    alarmEventItem.setDeviceCategoryName(deviceCategory.getName());
                }
            }
            if (o.getDeviceCurrentAlarmDto() != null && o.getDeviceCurrentAlarmDto().getAlarmDetailJson() != null) {
                AlarmRuleDto parse = JacksonUtils.parse(o.getDeviceCurrentAlarmDto().getAlarmDetailJson(), AlarmRuleDto.class);
                if (parse != null) {
                    alarmEventItem.setRuleCode(parse.getCode());
                    alarmEventItem.setRuleName(parse.getName());
                }

            }
            alarmEventItem.setStartTime(o.getStartTime());
            return alarmEventItem;
        }).collect(Collectors.toList());

        Page<FireSafetyAlarmEventVo.AlarmEventItem> page = new Page<>(request.getPageNo(), request.getPageSize(), total, voList);
        result.setPage(page);

        Map<String, Object> params = request.getParams();
        params.put("startTime", DateUtils.format(DateUtil.beginOfDay(new Date(currentTimeMillis)), DateUtils.FormatType.COMMON));
        params.put("endTime", DateUtils.format(DateUtil.endOfDay(new Date(currentTimeMillis)), DateUtils.FormatType.COMMON));
        result.setAlarmCountToday(mongoDeviceService.queryDeviceAlarmLogTotal(params));
        return Response.<FireSafetyAlarmEventVo>ok().wrap(result);
    }

}
